﻿package cz.dadul.adventure.model 
{
	
	import cz.dadul.adventure.model.*;
	import flash.display.*;
	/**
	 * ...
	 * @author Dadul
	 */
	public class ElementActionSet implements IActionable
	{
		public static const DEFAULT:String = "default";	
		public static const ACTION_GOTO_AND_PLAY:String = "gotoAndPlay";
		public static const ACTION_GOTO_AND_STOP:String = "gotoAndStop";
		//---------------------------------------------
		protected var _actionItems: Vector.<ElementActionItem>;	
		
		public function ElementActionSet()	{ 
			this._actionItems = new Vector.<ElementActionItem>;	
		}		
		
		/*
		 * Public
		 */ 
		public function addAction(target: String, action: String, data: * = null):ElementActionSet {
			// Run internal integrity test
			ElementActionSet.checkIntegrity(target, action, data);
			
			// Add ActionItem
			this._actionItems.push(new ElementActionItem(target, action, data));
			return this;
		}
		 
		
		public function perform():Boolean {
			var b:Boolean = true;
			// Go through all actions and perform them 
			for (var i:uint = 0; i < _actionItems.length; i++) {
				// If the item was not performed, set output to false
				if (!performItem(this._actionItems[i])) b = false;
			}
			
			return b; 
		}	 
		
		/*
		 * Protected functions
		 */ 
		protected function performItem(item:ElementActionItem):Boolean {		
			// Check if the action is found target element
			try {
				if (!(item.target[item.action] is Function)) return false;	
			} catch (e: Error) { 
				return false;
			}			
			
			switch(item.action) {
				// Special actions that needs extra handling
				case(ACTION_GOTO_AND_PLAY):
				case(ACTION_GOTO_AND_STOP):
					item.root[item.action](item.data, item.targetName, item.target);
				break;
				// All default actions
				default:
					// Proceed
					if (item.data != null) {
						// Pass the argument
						item.target[item.action](item.data);	
					} else {
						// Without arguments
						item.target[item.action]();	
					}
				break;
			}
			return true;
		}
		
	 
		/*
		 * Static functions
		 */
		/**
		 * Adds STANDART TIMELINE LOGIC ElementActionSet to the logic array defined by [component][source][state = "DEFAULT"]. If there is already some ActionSet, the new set is
		 * concatenated. 
		 *
		 * 
		 * @param	destArray	destination logic array
		 * @param	actionSet	behaviour 
		 * @param	component	[component]
		 * @param	source		[component][source]
		 * @param	state		[component][source][state] *OPTIONAL*, default = ElementActionSet.DEFAULT
		 * @return	array 		destination logic array
		 */
		 public static function addTimelineLogicSet(destArray:Array, actionSet:ElementActionSet, component:String, source:String, state:String = ElementActionSet.DEFAULT) : Array{
			// Cascade creation of the logic 
			if (destArray[component] == null) {
				destArray[component] = [];
			}
			if (destArray[component][source] == null) {
				destArray[component][source] = [];
			}
			if (destArray[component][source][state] == null) {
				// Create new ElementActionSet
				destArray[component][source][state] = actionSet;				
			} else {			
				// Concatenate with already existing actionItems				
				destArray[component][source][state]._actionItems = destArray[component][source][state]._actionItems.concat(actionSet._actionItems);
			}
			return destArray;
		}
		
		/**
		 * Adds CLICKABLE LOGIC ElementActionSet to the logic array defined by [state = "DEFAULT"]. If there is already some ActionSet, the new set is
		 * concatenated. 
		 *
		 * 
		 * @param	destArray	destination logic array
		 * @param	actionSet	behaviour 
		 * @param	state		[state] *OPTIONAL*, default = ElementActionSet.DEFAULT
		 * @return	array 		destination logic array
		 */
		public static function addClickableLogicSet(destArray:Array, actionSet:ElementActionSet, state:String = ElementActionSet.DEFAULT) : Array{
			if (destArray[state] == null) {
				// Create new ElementActionSet
				destArray[state] = actionSet;				
			} else {			
				// Concatenate with already existing actionItems				
				destArray[state]._actionItems = destArray[state]._actionItems.concat(actionSet._actionItems);
			}
			return destArray;
		}
		
		/**
		 * Performs basic integrity check for:
		 * - target existence
		 * - possible data action integrity (e.g.: frame label existence for goto actions)
		 * 
		 * @param	target
		 * @param	action
		 * @param	data
		 */
		protected static function checkIntegrity(target: String, action: String, data: * = null):void{
			// Check if the target exists
			Element.getActionComponentByName(target);					
			// Check if the target frame exists
			if((data != null)&&(data is String)){
				switch(action) {
					// Special actions that needs extra handling
					case(ACTION_GOTO_AND_PLAY):
					case(ACTION_GOTO_AND_STOP):
						/**
						 * TODO: Dodelat test existence action label
						 */
						var el:Element = Element.getTopComponent(target) as Element;
						if (el.getComponentLabel(target, data) == null) {
							throw new Error("Target ["+target+"] doesn't have defined frame label: "+data);
						}
						 
					break;
				}
			}
		
		}

		
		
		public function toString():String {
			var output:String = "";
			for (var i:uint = 0; i < _actionItems.length; i++) {
				// If the item was not performed, set output to false
				output += "ActionItem ["+i+"]: "+ this._actionItems[i] + ", ";
			}			
			return output;
		}
		
	}
	
}

/**
 * INTERAL CLASSES
 */

import cz.dadul.adventure.model.*;
import flash.display.*;

class ElementActionItem {
	private var _target: DisplayObject;
	private var _targetName:String;
	private var _root: DisplayObject;	
	private var _data:*;
	private var _action:String;
	
	public function ElementActionItem(target: String, action: String, data: * = null) {
		// Any data for the action function
		this._data = data;
		// Action function
		this._action = action;
		// Name of the target
		this._targetName = target;
		// Target object
		this._target = Element.getActionComponentByName(target);
		// Root of the target object (for special function calls)
		this._root = Element.getTopComponent(target);
	}
	
	
	public function toString():String {
		return "[ target: " + _target + ", action " + _action +  ", data: " + _data + "]"; 
	}
	
	/*
	 *	GETTERS / SETTERS
	 */ 

	public function get root():DisplayObject {
		return _root;
	}

	public function get target():DisplayObject {
		return _target;
	}
	public function get data():* {
		return _data;
	}
	public function get action():String {
		return _action;
	}
	public function get targetName():String {
		return _targetName;
	}

}
